import Web3 from "web3";
import { Account } from "web3-core";
import solc from "solc";
import { JsonRpcResponse } from "web3-core-helpers";

import * as RLP from "rlp";
import * as web3Utils from "web3-utils";

export const ERC20_BYTECODE =
  "0x608060405234801561001057600080fd5b50610024336202616061002960201b60201c565b610274565b600073ff" +
  "ffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156100" +
  "cc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020" +
  "018281038252601f8152602001807f45524332303a206d696e7420746f20746865207a65726f206164647265737300" +
  "81525060200191505060405180910390fd5b6100e5816002546101ec60201b610c7c1790919060201c565b60028190" +
  "5550610140816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffff" +
  "ffffffffffff168152602001908152602001600020546101ec60201b610c7c1790919060201c565b6000808473ffff" +
  "ffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190" +
  "8152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffff" +
  "ffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360" +
  "40518082815260200191505060405180910390a35050565b60008082840190508381101561026a576040517f08c379" +
  "a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b81" +
  "52602001807f536166654d6174683a206164646974696f6e206f766572666c6f770000000000815250602001915050" +
  "60405180910390fd5b8091505092915050565b610e3a806102836000396000f3fe6080604052348015610010576000" +
  "80fd5b50600436106100885760003560e01c806370a082311161005b57806370a08231146101fd578063a457c2d714" +
  "610255578063a9059cbb146102bb578063dd62ed3e1461032157610088565b8063095ea7b31461008d57806318160d" +
  "dd146100f357806323b872dd146101115780633950935114610197575b600080fd5b6100d960048036036040811015" +
  "6100a357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035" +
  "9060200190929190505050610399565b604051808215151515815260200191505060405180910390f35b6100fb6103" +
  "b7565b6040518082815260200191505060405180910390f35b61017d6004803603606081101561012757600080fd5b" +
  "81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffff" +
  "ffffffffffffffffffffffff169060200190929190803590602001909291905050506103c1565b6040518082151515" +
  "15815260200191505060405180910390f35b6101e3600480360360408110156101ad57600080fd5b81019080803573" +
  "ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061049a565b" +
  "604051808215151515815260200191505060405180910390f35b61023f6004803603602081101561021357600080fd" +
  "5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061054d565b6040" +
  "518082815260200191505060405180910390f35b6102a16004803603604081101561026b57600080fd5b8101908080" +
  "3573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610595" +
  "565b604051808215151515815260200191505060405180910390f35b610307600480360360408110156102d1576000" +
  "80fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092" +
  "9190505050610662565b604051808215151515815260200191505060405180910390f35b6103836004803603604081" +
  "101561033757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190" +
  "803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610680565b60405180828152" +
  "60200191505060405180910390f35b60006103ad6103a6610707565b848461070f565b6001905092915050565b6000" +
  "600254905090565b60006103ce848484610906565b61048f846103da610707565b61048a8560405180606001604052" +
  "8060288152602001610d7060289139600160008b73ffffffffffffffffffffffffffffffffffffffff1673ffffffff" +
  "ffffffffffffffffffffffffffffffff1681526020019081526020016000206000610440610707565b73ffffffffff" +
  "ffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260" +
  "200160002054610bbc9092919063ffffffff16565b61070f565b600190509392505050565b60006105436104a76107" +
  "07565b8461053e85600160006104b8610707565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffff" +
  "ffffffffffffffffffffffffffffffff16815260200190815260200160002060008973ffffffffffffffffffffffff" +
  "ffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205461" +
  "0c7c90919063ffffffff16565b61070f565b6001905092915050565b60008060008373ffffffffffffffffffffffff" +
  "ffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490" +
  "50919050565b60006106586105a2610707565b8461065385604051806060016040528060258152602001610de16025" +
  "9139600160006105cc610707565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffff" +
  "ffffffffffffffffffff16815260200190815260200160002060008a73ffffffffffffffffffffffffffffffffffff" +
  "ffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054610bbc90929190" +
  "63ffffffff16565b61070f565b6001905092915050565b600061067661066f610707565b8484610906565b60019050" +
  "92915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffff" +
  "ffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffff" +
  "ff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b" +
  "600033905090565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffff" +
  "ffffffffffffff161415610795576040517f08c379a000000000000000000000000000000000000000000000000000" +
  "0000008152600401808060200182810382526024815260200180610dbd6024913960400191505060405180910390fd" +
  "5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff" +
  "16141561081b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004" +
  "01808060200182810382526022815260200180610d286022913960400191505060405180910390fd5b806001600085" +
  "73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260" +
  "200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffff" +
  "ffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffff" +
  "ffffffff168373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd03" +
  "14c0f7b2291e5b200ac8c7c3b925836040518082815260200191505060405180910390a3505050565b600073ffffff" +
  "ffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16141561098c57" +
  "6040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182" +
  "810382526025815260200180610d986025913960400191505060405180910390fd5b600073ffffffffffffffffffff" +
  "ffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415610a12576040517f08c379" +
  "a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602381" +
  "5260200180610d056023913960400191505060405180910390fd5b610a7d8160405180606001604052806026815260" +
  "2001610d4a602691396000808773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffff" +
  "ffffffffffffffffff16815260200190815260200160002054610bbc9092919063ffffffff16565b6000808573ffff" +
  "ffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190" +
  "815260200160002081905550610b10816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffff" +
  "ffffffffffffffffffffffffffffffff16815260200190815260200160002054610c7c90919063ffffffff16565b60" +
  "00808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16" +
  "8152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffff" +
  "ffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df5" +
  "23b3ef836040518082815260200191505060405180910390a3505050565b6000838311158290610c69576040517f08" +
  "c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283" +
  "818151815260200191508051906020019080838360005b83811015610c2e578082015181840152602081019050610c" +
  "13565b50505050905090810190601f168015610c5b5780820380516001836020036101000a03191681526020019150" +
  "5b509250505060405180910390fd5b5060008385039050809150509392505050565b60008082840190508381101561" +
  "0cfa576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060" +
  "20018281038252601b8152602001807f536166654d6174683a206164646974696f6e206f766572666c6f7700000000" +
  "0081525060200191505060405180910390fd5b809150509291505056fe45524332303a207472616e7366657220746f" +
  "20746865207a65726f206164647265737345524332303a20617070726f766520746f20746865207a65726f20616464" +
  "7265737345524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e63654552433230" +
  "3a207472616e7366657220616d6f756e74206578636565647320616c6c6f77616e636545524332303a207472616e73" +
  "6665722066726f6d20746865207a65726f206164647265737345524332303a20617070726f76652066726f6d207468" +
  "65207a65726f206164647265737345524332303a2064656372656173656420616c6c6f77616e63652062656c6f7720" +
  "7a65726fa265627a7a723158208bfd5c482ccbde97b679a417e6f0a1698bb5491e25bb256602f6c4271b11757b6473" +
  "6f6c63430005110032";
export const GERALD_PRIVATE_KEY =
  "0x99B3C12287537E38C90A9219D4CB074A89A16E9CDB20BF85728EBD97C343E342";
export const STORAGE_SLOT = "0";

export let web3: Web3;
export let gerald: Account & { storageKey: string };

export const contractAddress = (makerAddress: string, nonce: number) => {
  if (!web3) {
    throw new Error("Must run web3.init() first");
  }
  return (
    "0x" +
    web3.utils
      .sha3(RLP.encode([makerAddress, nonce]) as any)
      .slice(12)
      .substring(14)
  );
};

export const importAccount = (privateKey: string) => {
  if (!web3) {
    throw new Error("Must run web3.init() first");
  }
  const account = web3.eth.accounts.privateKeyToAccount(privateKey);
  const mapStorageSlot = STORAGE_SLOT.padStart(64, "0");
  const mapKey = account.address.toString().substring(2).padStart(64, "0");
  const storageKey = web3Utils.sha3("0x".concat(mapKey.concat(mapStorageSlot)));
  return { ...account, storageKey };
};

export function compileSolidity(contractContent: string, contractName: string = "Test") {
  let result = JSON.parse(
    solc.compile(
      JSON.stringify({
        language: "Solidity",
        sources: {
          "main.sol": {
            content: contractContent,
          },
        },
        settings: {
          outputSelection: {
            "*": {
              "*": ["*"],
            },
          },
        },
      })
    )
  );

  const contract = result.contracts["main.sol"][contractName];
  return {
    bytecode: "0x" + contract.evm.bytecode.object,
    contract,
  };
}

// make a web3 request, adapted to manual seal testing
export async function customRequest(method: string, params: any[]) {
  return new Promise<JsonRpcResponse>((resolve, reject) => {
    (web3.currentProvider as any).send(
      {
        jsonrpc: "2.0",
        id: 1,
        method,
        params,
      },
      (error: Error | null, result?: JsonRpcResponse) => {
        if (error) {
          reject(
            `Failed to send custom request (${method} (${params.join(",")})): ${
              error.message || error.toString()
            }`
          );
        }
        resolve(result);
      }
    );
  });
}

export const init = (url = "http://localhost:9944") => {
  web3 = new Web3(url);
  gerald = importAccount(GERALD_PRIVATE_KEY);
  return web3;
};
